package m.common.model.util;

import m.common.model.Model;
import m.system.exception.MException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class QueryCondition {

	private enum OperType {
		EQ,MANYEQ,LIKE,ISNULL,NOTNULL,ISEMPTY,NOTEMPTY,GT,GE,LT,LE,BETWEEN,IN,NOTIN,INS,INSQL,NOTINSQL,OR,AND,NOT,EXP
	}
	
	private String name;
	private final OperType oper;
	private Object value;
	private boolean isField;
	private QueryCondition[] conditions;
	
	private QueryCondition(String name,OperType oper,Object value){
		this.name=name;
		this.oper=oper;
		this.value=value;
		this.isField=false;
	}
	private QueryCondition(String[] names,OperType oper) throws MException{
		if(names.length!=2) throw new MException("names数组个数为2!");
		this.name=names[0];
		this.oper=oper;
		this.value=names[1];
		this.isField=true;
	}
	private QueryCondition(QueryCondition[] conditions,OperType oper){
		List<QueryCondition> ls=new ArrayList<>();
		for(QueryCondition cond : conditions){
			if(null!=cond) ls.add(cond);
		}
		this.conditions=ls.toArray(new QueryCondition[0]);
		this.oper=oper;
	}
	protected QueryParameter toQueryParameter(String a,Object clazz,QueryList queryList) throws MException{
		StringBuilder sql=new StringBuilder();
		List<Object> valueList= new ArrayList<>();
		if(null!=conditions){
			if(this.oper.equals(OperType.AND)){
				for(int i=0;i<this.conditions.length;i++){
					QueryParameter qp=this.conditions[i].toQueryParameter(a,clazz, queryList);
					if(!qp.getSql().isEmpty()){
						sql.append(i==0?"":" AND ").append(qp.getSql());
						valueList.addAll(qp.getValueList());
					}
				}
				if(sql.length()>0) sql.insert(0, "(").append(")");
			}else if(this.oper.equals(OperType.OR)){
				for(int i=0;i<this.conditions.length;i++){
					QueryParameter qp=this.conditions[i].toQueryParameter(a,clazz, queryList);
					if(!qp.getSql().isEmpty()){
						sql.append(i==0?"":" OR ").append(qp.getSql());
						valueList.addAll(qp.getValueList());
					}
				}
				if(sql.length()>0) sql.insert(0, "(").append(")");
			}else if(this.oper.equals(OperType.NOT)){
				if(null!=this.conditions[0]){
					QueryParameter qp=this.conditions[0].toQueryParameter(a,clazz, queryList);
					if(!qp.getSql().isEmpty()){
						sql.append(" (NOT ").append(qp.getSql()).append(")");
						valueList.addAll(qp.getValueList());
					}
				}
			}
		}else if(this.isField){
			String fn="";
			String fv="";
			if(queryList instanceof ModelQueryList){
				fn=((ModelQueryList)queryList).getFieldNameSql(a, this.name, (Class<? extends Model>) clazz, false,this.name);
				fv=((ModelQueryList)queryList).getFieldNameSql(a, this.value.toString(), (Class<? extends Model>) clazz, false,this.value.toString());
				fn=((ModelQueryList)queryList).toExpressionField(fn,"");
				fv=((ModelQueryList)queryList).toExpressionField(fv,"");
			}
			if(this.oper.equals(OperType.EQ)){
				sql.append(" ").append(fn).append("=").append(fv).append(" ");
			}else if(this.oper.equals(OperType.MANYEQ)){
				sql.append(" INSTR(CONCAT(',',").append(fn).append(",','),CONCAT(',',").append(fv).append(",','))>0 ");
			}else if(this.oper.equals(OperType.LIKE)){
				sql.append(" INSTR(").append(fn).append(",").append(fv).append(")>0 ");
			}else if(this.oper.equals(OperType.GT)){
				sql.append(" ").append(fn).append(">").append(fv).append(" ");
			}else if(this.oper.equals(OperType.GE)){
				sql.append(" ").append(fn).append(">=").append(fv).append(" ");
			}else if(this.oper.equals(OperType.LT)){
				sql.append(" ").append(fn).append("<").append(fv).append(" ");
			}else if(this.oper.equals(OperType.LE)){
				sql.append(" ").append(fn).append("<=").append(fv).append(" ");
			}
		}else if(this.oper.equals(OperType.EXP)){
			String exp=this.name;
			Pattern pattern=Pattern.compile("\\#\\{.+?\\}");
			Matcher matcher=pattern.matcher(exp);
			while(matcher.find()){
				String str=matcher.group();
				String ns=str.substring(2,str.length()-1);
				String fn="";
				if(queryList instanceof ModelQueryList){
					fn=((ModelQueryList)queryList).getFieldNameSql(a, ns, (Class<? extends Model>) clazz, false,ns);
					fn=((ModelQueryList)queryList).toExpressionField(fn,"");
				}
				exp = exp.replace(str, fn);
			}
			sql.append(exp);
			valueList.addAll(Arrays.asList((Object[]) this.value));
		}else{
			String fn="";
			String tn="";
			if(queryList instanceof ModelQueryList){
				fn=((ModelQueryList)queryList).getFieldNameSql(a, this.name, (Class<? extends Model>) clazz, false,this.name);
				tn=((ModelQueryList)queryList).getFieldType(fn);
				fn=((ModelQueryList)queryList).toExpressionField(fn,"");
			}
			Object val=this.value;
			if(val instanceof Date && (tn.equals("INT") || tn.equals("LONG"))){
				val=((Date)val).getTime();
			}
			if(this.oper.equals(OperType.EQ)){
				sql.append(" ").append(fn).append("=? ");
				valueList.add(val);
			}else if(this.oper.equals(OperType.MANYEQ)){
				sql.append(" INSTR(CONCAT(',',").append(fn).append(",','),CONCAT(',',?,','))>0 ");
				valueList.add(val);
			}if(this.oper.equals(OperType.LIKE)){
				sql.append(" INSTR(").append(fn).append(",?)>0 ");
				valueList.add(val);
			}else if(this.oper.equals(OperType.ISNULL)){
				sql.append(" ").append(fn).append(" IS NULL ");
			}else if(this.oper.equals(OperType.NOTNULL)){
				sql.append(" ").append(fn).append(" IS NOT NULL ");
			}else if(this.oper.equals(OperType.ISEMPTY)){
				sql.append(" (").append(fn).append(" IS NULL OR ").append(fn).append("='')");
			}else if(this.oper.equals(OperType.NOTEMPTY)){
				sql.append(" (").append(fn).append(" IS NOT NULL AND ").append(fn).append("!='')");
			}else if(this.oper.equals(OperType.GT)){
				sql.append(" ").append(fn).append(">? ");
				valueList.add(val);
			}else if(this.oper.equals(OperType.GE)){
				sql.append(" ").append(fn).append(">=? ");
				valueList.add(val);
			}else if(this.oper.equals(OperType.LT)){
				sql.append(" ").append(fn).append("<? ");
				valueList.add(val);
			}else if(this.oper.equals(OperType.LE)){
				sql.append(" ").append(fn).append("<=? ");
				valueList.add(val);
			}else if(this.oper.equals(OperType.IN)){
				QueryParameter qp=((ModelQueryList) val).getQueryParameter();
				sql.append(" ").append(fn).append(" IN(").append(qp.getSql()).append(") ");
				valueList.addAll(qp.getValueList());
			}else if(this.oper.equals(OperType.INSQL)){
				QueryParameter qp=(QueryParameter) val;
				sql.append(" ").append(fn).append(" IN(").append(qp.getSql()).append(") ");
				valueList.addAll(qp.getValueList());
			}else if(this.oper.equals(OperType.NOTIN)){
				QueryParameter qp=((ModelQueryList) val).getQueryParameter();
				sql.append(" ").append(fn).append(" NOT IN(").append(qp.getSql()).append(") ");
				valueList.addAll(qp.getValueList());
			}else if(this.oper.equals(OperType.NOTINSQL)){
				QueryParameter qp=(QueryParameter) val;
				sql.append(" ").append(fn).append(" NOT IN(").append(qp.getSql()).append(") ");
				valueList.addAll(qp.getValueList());
			}else if(this.oper.equals(OperType.INS)){
				Object[] objs=(Object[]) val;
				StringBuilder ins=new StringBuilder();
				if(objs.length>0) {
					for(Object obj : objs){
						ins.append(",?");
						valueList.add(obj);
					}
				}else {
					ins.append(",''");
				}
				sql.append(" ").append(fn).append(" IN(").append(ins.substring(1)).append(")") ;
			}else if(this.oper.equals(OperType.BETWEEN)){
				Object[] objs=(Object[]) val;
				sql.append(" (").append(fn).append(" BETWEEN ? AND ?) ");
				valueList.add(objs[0]);
				valueList.add(objs[1]);
			}
		}
		return new QueryParameter(sql.toString(),valueList);
	}
	/**
	 * 等于
	 *  name
	 *  value
	 * 
	 */
	public static QueryCondition eq(String name,Object value){
		return new QueryCondition(name,OperType.EQ,value);
	}
	public static QueryCondition eq(String[] names) throws MException{
		return new QueryCondition(names,OperType.EQ);
	}
	/**
	 * 等于 生成多个QueryCondition
	 *  name
	 *  value
	 *
	 */
	public static List<QueryCondition> eqs(String name,Object... value){
		List<QueryCondition> qcs= new ArrayList<>();
		for (Object o : value) {
			qcs.add(eq(name, o));
		}
		return qcs;
	}

	/**
	 * 多等于   a,b  匹配  a
	 */
	public static QueryCondition manyEq(String name,Object value){
		return new QueryCondition(name,OperType.MANYEQ,value);
	}
	public static QueryCondition manyEq(String[] names) throws MException {
		return new QueryCondition(names,OperType.MANYEQ);
	}
	/**
	 * 多等于 生成多个QueryCondition
	 *  name
	 *  value
	 *
	 */
	public static List<QueryCondition> manyEqs(String name,Object... value){
		List<QueryCondition> qcs= new ArrayList<>();
		for (Object o : value) {
			qcs.add(manyEq(name, o));
		}
		return qcs;
	}

	/**
	 * 包含
	 *  name
	 *  value
	 * 
	 */
	public static QueryCondition like(String name,Object value){
		return new QueryCondition(name,OperType.LIKE,value);
	}
	public static QueryCondition like(String[] names) throws MException{
		return new QueryCondition(names,OperType.LIKE);
	}
	/**
	 * 包含 生成多个QueryCondition
	 *  name
	 *  value
	 * 
	 */
	public static List<QueryCondition> manyLike(String name,Object... value){
		List<QueryCondition> qcs= new ArrayList<>();
		for (Object o : value) {
			qcs.add(like(name, o));
		}
		return qcs;
	}
	/**
	 * 为null
	 *  name
	 * 
	 */
	public static QueryCondition isNull(String name){
		return new QueryCondition(name,OperType.ISNULL,null);
	}
	/**
	 * 不为null
	 *  name
	 * 
	 */
	public static QueryCondition notNull(String name){
		return new QueryCondition(name,OperType.NOTNULL,null);
	}
	/**
	 * 为空
	 *  name
	 * 
	 */
	public static QueryCondition isEmpty(String name){
		return new QueryCondition(name, OperType.ISEMPTY,null);
	}
	/**
	 * 不为空
	 *  name
	 * 
	 */
	public static QueryCondition notEmpty(String name){
		return new QueryCondition(name, OperType.NOTEMPTY,null);
	}
	/**
	 * 大于
	 *  name
	 *  value
	 * 
	 */
	public static QueryCondition gt(String name,Object value){
		return new QueryCondition(name,OperType.GT,value);
	}
	public static QueryCondition gt(String[] names) throws MException{
		return new QueryCondition(names,OperType.GT);
	}
	/**
	 * 大于等于
	 *  name
	 *  value
	 * 
	 */
	public static QueryCondition ge(String name,Object value){
		return new QueryCondition(name,OperType.GE,value);
	}
	public static QueryCondition ge(String[] names) throws MException{
		return new QueryCondition(names,OperType.GE);
	}
	/**
	 * 小于
	 *  name
	 *  value
	 * 
	 */
	public static QueryCondition lt(String name,Object value){
		return new QueryCondition(name,OperType.LT,value);
	}
	public static QueryCondition lt(String[] names) throws MException{
		return new QueryCondition(names,OperType.LT);
	}
	/**
	 * 小于等于
	 *  name
	 *  value
	 * 
	 */
	public static QueryCondition le(String name,Object value){
		return new QueryCondition(name,OperType.LE,value);
	}
	public static QueryCondition le(String[] names) throws MException{
		return new QueryCondition(names,OperType.LE);
	}
	/**
	 * in(select)
	 *  name
	 *  value
	 * 
	 */
	public static QueryCondition in(String name,ModelQueryList value){
		return new QueryCondition(name,OperType.IN,value);
	}
	public static QueryCondition in(String name,QueryParameter value){
		return new QueryCondition(name,OperType.INSQL,value);
	}
	
	/**
	 * not in(select)
	 *  name
	 *  value
	 * 
	 */
	public static QueryCondition notIn(String name,ModelQueryList value){
		return new QueryCondition(name,OperType.NOTIN,value);
	}
	public static QueryCondition notIn(String name,QueryParameter value){
		return new QueryCondition(name,OperType.NOTINSQL,value);
	}
	/**
	 * in(?,?...)
	 *  name
	 *  values
	 * 
	 */
	public static QueryCondition in(String name,Object... values){
		return new QueryCondition(name,OperType.INS,values);
	}
	/**
	 * 在两个value之间
	 *  name
	 *  values
	 * 
	 *  MException
	 */
	public static QueryCondition between(String name,Object[] values) throws MException{
		if(values.length!=2) throw new MException("values数组个数为2!");
		return new QueryCondition(name,OperType.BETWEEN,values);
	}
	/**
	 * 多个条件按or连接
	 *  conditions
	 * 
	 */
	public static QueryCondition or(QueryCondition... conditions){
		return null!=conditions&&conditions.length>0?new QueryCondition(conditions,OperType.OR):null;
	}
	/**
	 * 多个条件按and连接
	 *  conditions
	 * 
	 */
	public static QueryCondition and(QueryCondition... conditions){
		return null!=conditions&&conditions.length>0?new QueryCondition(conditions,OperType.AND):null;
	}
	/**
	 * 条件取反
	 *  condition
	 * 
	 */
	public static QueryCondition not(QueryCondition condition){
		if(null!=condition){
			return new QueryCondition(new QueryCondition[]{condition},OperType.NOT);
		}else{
			return null;
		}
	}

	/**
	 * 表达式条件
	 *  exp 表达式字段示例：#{field.name}
	 *  values
	 */
	public static QueryCondition exp(String exp,Object... values){
		return new QueryCondition(exp,OperType.EXP,values);
	}
	
}
